// Definition of a template to keep track of a set of objects
// Copyright 2005 (C) Ralph Thomas

#ifndef UTIL_STATIC_MANAGER_H
#define UTIL_STATIC_MANAGER_H

#include <map>
#include <util/string.h>

namespace util {
	//
	/// A "static managed" object has a static "fromHash" method
	/// which returns an object for a hash. If an object already
	/// exists for that hash, then it's refcount is increased and
	/// it is returned. When the object is deleted it is removed
	/// from the "fromHash" method.
	///
	/// To use this class you should write something like this:
	///  class myClass : public staticManaged<myClass*> { ...
	//
	template<typename T> class staticManaged {
		typedef std::map<util::string, T*> refMap;
		typedef typename std::map<util::string, T*>::iterator refMapIter;

		static refMap _map;
		unsigned int _refCount;
		util::string _myHash;
	  protected:
		//
		// staticManaged( util::string hash )
		//
		/// Create a new refManager with the given hash key.
		///
		/// \param	hash	the hash key for this refManager.
		//
		staticManaged( util::string hash ) :
		 _refCount( 1 ), _myHash( hash ) {}
	  public:
		//
		// T fromHash( util::string hash )
		//
		/// Return a new T object for the given hash string. If
		/// a T object already exists for the given hash then it's
		/// reference count is increased and it is returned.
		///
		/// \param	hash	the hash string.
		///
		/// \return	a new or referenced object.
		//
		static T* fromHash( util::string hash ) {
			//
			// First try to locate something associated with this
			// hash in our map.
			//
			refMapIter i = _map.find( hash );
			if( i == _map.end() ) {
				//
				// We don't already have an object for this
				// hash.
				//
				T* myT = new T( hash );
				_map[hash] = myT;
				return myT;
			}
			//
			// We found one -- increase it's refcount and return
			// it.
			//
			return i->second->retain();
		}
		//
		// ~staticManaged
		//
		/// This destructor removes this object from the map.
		//
		virtual ~staticManaged() {
			refMapIter i = _map.find( _myHash );
			if( i != _map.end() ) _map.erase( i );
		}
		//
		// T* retain()
		//
		/// Increase the reference count and return this. The
		/// operation is const because the semantics of "retain"
		/// are very close to copy (and are identical for objects
		/// which have no state).
		//
		T* retain() const {
			staticManaged<T>* me =
				const_cast<staticManaged<T>*>( this );
			me->_refCount++;
			return static_cast<T*>( me );
		}
		//
		// void release()
		//
		/// Decrease the reference count, delete this if it hits zero.
		//
		void release() { _refCount--; if( !_refCount ) delete this; }
	};
	//
	/// Initializer for static map data.
	//
	template<typename T> typename staticManaged<T>::refMap staticManaged<T>::_map;
};
#endif

